open Ast
open Type
open Common


(*let s_from_path p =
  (if List.length (fst p) = 0 then
    snd p
  else
    String.concat "." (fst p) ^ snd p
  )
*)

let g_com = ref (Common.create 5)

let s_fun_args (arg, o, t) =
  arg

let s_from_path path =
  match fst path, snd path with
      | [], "Int" -> "int"
  | [], "Float" -> "float"
      | [], s -> s
      | el, s -> String.concat "." el ^ "." ^ s
;;

let rec is_string_type t =
  match follow t with
  | TInst ({cl_path = ([], "String")}, _) -> true
  | TAnon a ->
     (match !(a.a_status) with
     | Statics ({cl_path = ([], "String")}) -> true
     | _ -> false)
  | _ -> false

let is_string_expr e = is_string_type e.etype

let rec is_array_type t =
  match follow t with
  | TInst ({cl_path = ([], "Array")}, _) -> true
  | TAnon a ->
     (match !(a.a_status) with
     | Statics ({cl_path = ([], "Array")}) -> true
     | _ -> false)
  | _ -> false

let is_array_expr e = is_array_type e.etype

let printcode oc code =
  Printf.fprintf oc code

let newline oc =
  printcode oc "\n"

let should_end_set_array = ref 0
(* let rec represent_type t : string=
  match t with
  | TEnum (e, p) ->
    let args = ref [] in
    (*(List.iter (fun i -> List.append ["plop"] ["plop"]) ["plop"]);*)
    List.append [] [];
    ();
    s_from_path e.e_path
  | TInst (i, p) ->
    "unknown"
  | TType (typ, p) ->
    "unknown"
  | _ ->
    "unknown"
*)

let rec represent_type t short=
  (match (follow t) with
    | TInst (cl, params) ->
      (match short with
      | false ->
      let tor = ref "" in
      tor := s_from_path cl.cl_path;
      let p_helper p =
        tor := !tor ^ represent_type p false
      in
      List.iter p_helper params;
      !tor
      | true ->
        snd cl.cl_path
      )
    | TDynamic t ->
      "Object"
    | TEnum({ e_path = [],"Void" },[]) ->
      "void" 
    | TEnum({ e_path = [], "Bool" },[]) ->
      "Boolean"
    | TEnum (_ , _) ->
      "TEnum" 
    | _ -> ""
  )
let is_type_generic_in_class t c =
  let isit = ref false in
  let f_helper ct =
    (if String.compare (represent_type t false) (represent_type (snd ct) false) == 0 then
      isit := true
    )
  in
  List.iter f_helper c.cl_types;
  !isit

let rec generate_expr aset oc ex =
  match ex.eexpr with
  | TConst const ->
    print_endline("TConst");
    (
    match const with
    | TInt i ->
      printcode oc "%ld" i
    | TFloat f ->
      printcode oc "%s" f
    | TString s ->
      if String.contains s '\000' then error "A String cannot contain \\0 characters" ex.epos;
      printcode oc "\"%s\"" (Ast.s_escape s)
    | TBool b ->
      printcode oc "%s" (if b then "true" else "false")
    | TNull ->
      printcode oc "%s" "null"
    | TThis ->
      printcode oc "%s" "this"
    | TSuper ->
      printcode oc "%s" "super"
    )
  | TLocal l ->
    print_endline("TLocal");
    printcode oc "%s" l
  | TEnumField _ ->
    print_endline("TEnumField")
  | TArray (ar, index) ->
    print_endline("TArray");
    generate_expr aset oc ar;
    (match aset with
    | false ->
      printcode oc "%s" ".__arrayaccessget("
    | true ->
      printcode oc "%s" ".__arrayaccessset("
    );
    generate_expr false oc index;
    (match aset with
    | true ->
      should_end_set_array := !should_end_set_array +1
    | false ->
      printcode oc "%s" ")"
    )
  | TBinop (op, expr1, expr2) ->
    let gen_operator oc op =
    match op with
    | OpAdd when (is_string_expr expr1 || is_string_expr expr2) ->
      print_endline "String addition";
      generate_expr false oc expr1;
      printcode oc "%s" ".concat(";
      generate_expr false oc expr2;
      printcode oc "%s" ")"
    | OpAdd ->
      generate_expr false oc expr1;
      printcode oc "%s" "+";
      generate_expr false oc expr2
      
    | OpMult ->
      generate_expr false oc expr1;
      printcode oc "%s" "*";
      generate_expr false oc expr2
    | OpDiv ->
      generate_expr false oc expr1;
      printcode oc "%s" "/";
      generate_expr false oc expr2
    | OpSub ->
      generate_expr false oc expr1;
      printcode oc "%s" "-";
      generate_expr false oc expr2
    | OpAssign ->
      let had_to_end_set_array = ref false in
      generate_expr true oc expr1;
      (match !should_end_set_array with
      | 0 ->
        printcode oc "%s" "="
      | _ ->
        printcode oc "%s" ",";
        had_to_end_set_array := true;
        should_end_set_array := !should_end_set_array -1
      );
      generate_expr false oc expr2;
      (if !had_to_end_set_array then
        printcode oc "%s" ")"
      )
    | OpEq ->
      generate_expr false oc expr1;
      printcode oc "%s" "==";
      generate_expr false oc expr2
    | OpNotEq ->
      generate_expr false oc expr1;
      printcode oc "%s" "!=";
      generate_expr false oc expr2
    | OpGt ->
      generate_expr false oc expr1;
      printcode oc "%s" ">";
      generate_expr false oc expr2
    | OpGte ->
      generate_expr false oc expr1;
      printcode oc "%s" ">=";
      generate_expr false oc expr2
    | OpLt ->
      generate_expr false oc expr1;
      printcode oc "%s" "<";
      generate_expr false oc expr2
    | OpLte ->
      generate_expr false oc expr1;
      printcode oc "%s" "<=";
      generate_expr false oc expr2
    | OpAnd ->
      generate_expr false oc expr1;
      printcode oc "%s" "&";
      generate_expr false oc expr2
    | OpOr ->
      generate_expr false oc expr1;
      printcode oc "%s" "|";
      generate_expr false oc expr2
    | OpXor ->
      ()
    | OpBoolAnd ->
      generate_expr false oc expr1;
      printcode oc "%s" " and ";
      generate_expr false oc expr2
    | OpBoolOr ->
      generate_expr false oc expr1;
      printcode oc "%s" " or ";
      generate_expr false oc expr2
    | OpShl ->
      ()
    | OpShr ->
      ()
    | OpUShr ->
      ()
    | OpMod ->
      generate_expr false oc expr1;
      printcode oc "%s" "%";
      generate_expr false oc expr2
    | OpAssignOp binop ->
      ()
    | OpInterval ->
      ()
    in
    print_endline("TBinop");
    (*generate_expr false oc expr1;*)
    gen_operator oc op

  | TField (expr, name) ->
    print_endline("TField");
    generate_expr false oc expr;
    printcode oc ".%s" name
  | TTypeExpr modu ->
    print_endline "TTypeExpr";
    (
    match modu with
    | TClassDecl c ->
      print_endline "TClassDecl";
      printcode oc "%s" (s_from_path c.cl_path)
    | TEnumDecl e ->
      print_endline "TEnumDecl"
    | TTypeDecl t ->
      print_endline "TTypeDecl"
    )

  | TParenthesis e ->
    print_endline "TParenthesis";
    printcode oc "%s" "(";
    generate_expr false oc e;
    printcode oc "%s" ")"
  | TObjectDecl _ ->
    print_endline "TObjectDecl"
  | TArrayDecl _ ->
    print_endline "TArrayDecl"
  | TCall (called, args) (*when called.eexpr <> TLocal "__jfx__"*) ->
    let gen_call oc called args =
    match called.eexpr with
    | TLocal "__jfx__" ->
      print_endline "__jfx__";
      (
      match (List.hd args).eexpr with 
      | TConst c ->
        print_endline ("JFX args")
        (*printcode oc "%s\n" st*)
      | _ ->
        print_endline "JFX Without args";
        ()
      );
      let helper exp =
        generate_expr false oc exp; () in
      List.iter helper args
    | _ ->
      print_endline "Calling local <> jfx";
      generate_expr false oc called;
      printcode oc "%s" "(";
      let count = ref 0 in
      let helper exp=
        count := !count+1;
        (if !count > 1 then
          printcode oc "%s" ","
        );
        generate_expr false oc exp; ()
      in
      List.iter helper args;
      printcode oc "%s" ")"
    in
    gen_call oc called args
  | TNew (cl, nparams, args) ->
    print_endline("TNew");
    (match cl.cl_path, args with
    (*| ([], "String"), _ ->
      printcode "\"%s\"" (Ast.s_escape args)*)
    (*| ([], "Array"), args ->*)
    | (_,_), _ ->
      printcode oc "new %s" (s_from_path cl.cl_path);
(* BEGINNING OF GENERICS *)
      (if List.length nparams > 0 then
        printcode oc "%s" "<"
      );
      let count = ref 0 in
      let generics_helper h =
        (if !count > 0 then
          printcode oc "%s" ","
        );
        printcode oc "%s" (represent_type h false);
        count := !count +1
      in
      List.iter generics_helper nparams;
      (if List.length nparams > 0 then
        printcode oc "%s" ">"
      );

(* END OF GENERICS *)
      printcode oc "%s" "(";
      let count = ref 0 in
      let he arg =
        count := !count+1;
        (if !count > 1 then
          printcode oc "%s" ","
        );
        generate_expr false oc arg
      in
      List.iter he args;
      printcode oc "%s" ")"
    )
  | TUnop (op, Ast.Prefix, e) ->
    print_endline "TUnop Prefix"
  | TUnop (op, Ast.Postfix, e) ->
    print_endline "TUnop Postfix";
    generate_expr false oc e;
    printcode oc "%s" (Ast.s_unop op)
  | TFunction tfun ->
    print_endline("---TFunction");
    generate_expr false oc tfun.tf_expr
  | TVars vars ->
    print_endline("TVars");
    let generate_var (n,t,v) =
      (match (follow t) with
      | TInst (cl, params) ->
        printcode oc "%s "  (s_from_path cl.cl_path)
      | TDynamic t ->
        printcode oc "%s " "Object"
      | _ -> ()
      );
      printcode oc "%s;\n" n;
      (match v with
      | Some v ->
        printcode oc "%s = " n;
        generate_expr false oc v
      | None -> ()
      )
    in
    List.iter generate_var vars;
  | TBlock exprs ->
    print_endline("TBlock");
    printcode oc "%s" "{\n";
    let helper exp =
      generate_expr false oc exp;
    (match exp.eexpr with
    | TBlock _ -> ()
    | _ ->
      printcode oc "%s" ";\n"
    ) 
    (*printcode oc "%s" ";\n";*)
    in
    List.iter helper exprs;
    printcode oc "%s" "}\n"
  | TFor (v, t, it, e) ->
    print_endline("TFor");
    printcode oc "%s" "{\nvar __it__=";
    generate_expr false oc it;
    printcode oc "%s" "\n}" 
  | TIf (cond, e, eelse) ->
    print_endline("TIf");
    printcode oc "%s" "if (";
    generate_expr false oc cond;
    printcode oc "%s" ") ";
    generate_expr false oc e;
    (match eelse with
    | None -> ()
    | Some e when e.eexpr = TConst(TNull) -> 
      print_endline "TConst(TNull)";
      ()
    | Some e ->
      printcode oc "\n%s" "else ";
      generate_expr false oc e  
    );
  | TWhile (cond, e, Ast.NormalWhile) ->
    print_endline("TWhile");
    printcode oc "%s" "while( ";
    generate_expr false oc cond;
    printcode oc "%s" " )\n";
    generate_expr false oc e
  | TWhile (cond, e, Ast.DoWhile) ->
    print_endline("DoWhile")
  | TSwitch _ ->
    print_endline("TSwitch")
  | TMatch _ ->
    print_endline("TMatch")
  | TTry _ ->
    print_endline("TTry")
  | TReturn ex ->
    print_endline("TReturn");
    printcode oc "%s " "return";
    (match ex with
    | Some ex ->
      generate_expr false oc ex
    | None -> ()
    )
    (*printcode oc "%s" ";"*)
  | TBreak  ->
    print_endline("TBreak")
  | TContinue  ->
    print_endline("TContinue")
  | TThrow _ ->
    print_endline("TThrow")

let generate_static_field f oc static c =
  print_endline "GENERATING STATIC FIELD " ;
  print_endline f.cf_name;
  let expr = f.cf_expr in
  match follow f.cf_type with
  | TFun (args, returntype) ->
    print_endline "THIS IS ACTUALLY A FUNCTION";
    printcode oc "%s" "";
    printcode oc "%s" "public ";
    (if static then
      printcode oc "%s" "static "
    );
    
    (*printcode oc "%s" "function ";*)
    printcode oc "%s " (represent_type returntype false);
    printcode oc "%s" f.cf_name;
    printcode oc "%s" "(";
    let count = ref 0 in
    let infc (j, k, l) =
      count := !count+1;
      (if !count > 1 then
        printcode oc "%s" ","
      );
      (if is_type_generic_in_class l c then
        printcode oc "%s " (represent_type l true)
      else      
        printcode oc "%s " (represent_type l false)
      );
      printcode oc "%s" j
    in
    List.iter infc args;
    printcode oc "%s" ")";
    newline oc;
    (*printcode oc "%s" "{";*)
    newline oc;
    (match expr with
    | Some expr ->
      generate_expr false oc expr
    | None ->
      ()
    );
    (*printcode oc "%s" "}";*)
    newline oc;
  | TMono (_) -> print_endline "This is a TMono";
  | TEnum (te, par) -> print_endline "This is a TEnum";
  | TInst (tc, par) ->
    printcode oc "%s " "public";
    (if static then
      printcode oc "%s " "static"
    );
    printcode oc "%s " (represent_type f.cf_type false);
    printcode oc "%s" f.cf_name;
    (match expr with
    | Some expr ->
      printcode oc "%s" " = ";
      generate_expr false oc expr
    | None ->
      ()
    );
    printcode oc "%s" ";\n"
  | TType (l, tt) -> print_endline "This is a TType";
  | TAnon (t) -> print_endline "This is a TAnon";
  | TDynamic (t) -> print_endline "This is a TDynamic";
  | TLazy (t) -> print_endline "This is a TLazy"

let generate_class c t oc =
  let concat_path path =
    print_endline "CONCAT";
    let mpath = fst path in
    let ll = ref [] in
    for i = 0 to ((List.length mpath)-1) do
      ll := List.append !ll [List.nth mpath i];
      print_endline (String.concat "/" !ll);
      (try (Unix.mkdir (String.concat "/" !ll) 0o755) with _ -> ());
      (*print_endline "Tried to create " ^ (String.concat "/" !ll);*)
      ()
    done;
    String.concat "/" !ll
  in
  print_endline "Generating class ";
  concat_path c.cl_path;
  let path = "./" ^ (String.concat "/" (fst c.cl_path)) in
  print_endline path;
  let cfile = open_out (path ^ "/" ^ (snd c.cl_path) ^ ".java") in
  print_endline (snd c.cl_path);
  let package_name = (s_from_path ((fst c.cl_path),"")) in
  let print_package packname =
  match packname with
  | "" ->
    ()
  | _ ->
    printcode cfile "package %s;\n" (String.sub packname 0 ((String.length packname)-1)); ()
  in
  print_package package_name;
  (*printcode cfile "%s" (s_from_path (List.nth c.cl_path (List.length-1)));*)
  printcode cfile "%s" "public class ";
  let path = s_from_path c.cl_path in
  printcode cfile "%s" (snd c.cl_path);
  (if List.length c.cl_types > 0 then
    printcode cfile "%s" "<"
  );
  let count = ref 0 in
  let generics_helper h =
    (if !count > 0 then
      printcode cfile "%s" ","
    );
    printcode cfile "%s" (fst h);
    count := !count +1
  in
  List.iter generics_helper c.cl_types;
  (if List.length c.cl_types > 0 then
    printcode cfile "%s" ">"
  );
  (match c.cl_super with
  | None -> ()
  | Some (sup, _) ->
    printcode cfile " extends %s" (s_from_path sup.cl_path);
  );
  (if List.length c.cl_implements > 0 then
    printcode cfile "%s" " implements ";
    (*List.iter (fun (name, opt, ty) -> printcode cfile "%s" (s_from_path c.cl_path)) c.cl_implements*)
  );
  newline cfile;
  printcode cfile "%s" "{";
  newline cfile;
  newline cfile;
  (* generate static fields *)
  List.iter (fun e -> generate_static_field e cfile true c) c.cl_ordered_statics;
  List.iter (fun e -> generate_static_field e cfile false c) c.cl_ordered_fields;
  let quick_view ty =
    print_endline "TYPE";
    print_endline (fst ty);
    print_endline (represent_type (snd ty) false)
  in
  List.iter quick_view c.cl_types;
  newline cfile;
  printcode cfile "%s" "}";
  newline cfile;
  ()

let generate_type t oc =
  match t with
  | TClassDecl c ->
    (*print_endline (String.concat "." (fst c.cl_path) ^ "." ^ snd c.cl_path);*)
    if not c.cl_extern then generate_class c t oc;
  | TEnumDecl e ->
    print_endline "Generating enum"
  | TTypeDecl t ->
    print_endline "Generating type"



let generate com = 
  g_com := com;
  if com.verbose then print_endline "Entering generation phase";
  let oc = open_out com.file in
  printcode oc ".assembly %s {}" com.file;
  newline oc;
  List.iter (fun e -> generate_type e oc) com.types;
  close_out oc;
  ()



